Characteristics of the meta-analytic reports
paste0("The ", unique(res_m$n_paper), " retained reports explored the effects of ",
length(unique(res_m$intervention_general)), " intervention types, on at least one of the ",
length(unique(res_m$outcome_general)), " possible outcomes.",
"These reports generated a total of ", nrow(res_m), " meta-analyses",
" that synthesized the evidence from more than ",
length(unique(paste0(dcct$author, dcct$year))), " CCTs, and that explored ", length(unique(res_m$PICO_amstar)), " unique PICOs.")
## [1] "The 53 retained reports explored the effects of 21 intervention types, on at least one of the 19 possible outcomes.These reports generated a total of 248 meta-analyses that synthesized the evidence from more than 206 CCTs, and that explored 147 unique PICOs."
res_year = res_m %>%
group_by(paper) %>%
slice(1)
paste0("These reports were, in their vast majority (",
propC(res_year$year_meta_num >= 2018), "%) published after 2018.")
## [1] "These reports were, in their vast majority (87%) published after 2018."
dat_pico_overlap = res_m %>%
group_by(PICO_amstar, Intervention, Outcome, Age) %>%
summarise(n=n()) %>%
filter(n > 1)
## `summarise()` has grouped output by 'PICO_amstar', 'Intervention', 'Outcome'.
## You can override using the `.groups` argument.
paste0("A total of ", nrow(dat_pico_overlap), " PICOs (", round(nrow(dat_pico_overlap)/length(unique(res_m$PICO_amstar)) * 100), "%) was explored in several overlapping meta-analyses. The PICOs with the highest number of overlapping meta-analyses were investigating the effects of ",
unique(dat_pico_overlap[dat_pico_overlap$n >= 6, ]$Intervention), " on ", tolower(paste(paste0(paste0(unique(dat_pico_overlap[dat_pico_overlap$n >= 6, ]$Outcome)), " (n=",
paste0(unique(dat_pico_overlap[dat_pico_overlap$n >= 6, ]$n)), ")")
, collapse=", ")))
## [1] "A total of 53 PICOs (36%) was explored in several overlapping meta-analyses. The PICOs with the highest number of overlapping meta-analyses were investigating the effects of PUFA on adhd symptoms (n=7), disruptive behaviors (n=8), restricted/repetitive behaviors (n=7), social-communication (n=8)"
# paste0("The mean number of overlapping meta-analyses on the same PICO was ",
# round(mean(dat_pico$n), 2), " (minimum = ",
# min(dat_pico$n), ", maximum = ",
# max(dat_pico$n), ").")
# paste0(length(unique(res_m$intervention_general)), " intervention types (", sort(collapsunique(res_m$intervention_general)), ")")
# paste0(length(unique(res_m$outcome_general)), " outcomes (", sort(collapsunique(res_m$outcome_general)), ")")
Primary
res_interventions = res_p %>%
group_by(Intervention) %>%
select(Outcome) %>%
distinct() %>%
summarise(n = n(),
outcome = collapsunique(Outcome)) %>%
arrange(desc(n))
## Adding missing grouping variables: `Intervention`
nrowInter = nrow(res_interventions)
table(res_p$amstar_rank)
##
## Critically low High Low
## 34 24 90
dat_amstar = as.data.frame(table(res_p %>% group_by(paper) %>% slice(1) %>% ungroup%>% select(amstar_rank)))
paste0("Thus, ", length(unique(res_m$PICO_amstar)), " meta-analyses, derived from ", length(unique(res_p$paper)), " reports, were finally included in the primary analysis after discarding overlapping meta-analyses.")
## [1] "Thus, 147 meta-analyses, derived from 31 reports, were finally included in the primary analysis after discarding overlapping meta-analyses."
paste0("According to the AMSTAR-2 scoring, ",
dat_amstar[dat_amstar$Var1=="High", "Freq"],
" of these ", length(unique(res_p$paper)),
" reports were of high quality, ",
dat_amstar[dat_amstar$Var1=="Low", "Freq"],
" of low quality, and ",
dat_amstar[dat_amstar$Var1=="Critically low", "Freq"],
" of critically low quality. ",
dat_amstar[dat_amstar$Var1=="Moderate", "Freq"], " moderate")
## [1] "According to the AMSTAR-2 scoring, 4 of these 31 reports were of high quality, 10 of low quality, and 17 of critically low quality. moderate"
paste0("The interventions that covered the largest number of outcomes were ", paste(paste0(res_interventions$Intervention[1:5], " (n=", res_interventions$n[1:5], "/",length(unique(res_p$Outcome)), ")"), collapse=", "))
## [1] "The interventions that covered the largest number of outcomes were OXYT (n=11/19), PUFA (n=11/19), AAI (n=10/19), ACUP (n=9/19), NAC (n=9/19)"
# paste0("The interventions that covered the smallest number of outcomes were ", paste(paste0(res_interventions$Intervention[(nrowInter-8):nrowInter], " (n=", res_interventions$n[(nrowInter-8):nrowInter], "/",length(unique(res_p$Outcome)), ")"), collapse=", "))
res_outcomes = res_p %>%
group_by(Outcome) %>%
select(Intervention) %>%
distinct() %>%
summarise(n = n(),
outcome = collapsunique(Intervention)) %>%
arrange(desc(n))
## Adding missing grouping variables: `Outcome`
nrowOutcome = nrow(res_outcomes)
paste0("and the outcomes that were covered by the largest number of interventions all regarded the efficacy of the interventions on core ASD symptoms: ", paste(paste0(tolower(res_outcomes$Outcome[1:5]), " (n=", res_outcomes$n[1:5], "/",length(unique(res_m$Intervention)), ")"), collapse=", "))
## [1] "and the outcomes that were covered by the largest number of interventions all regarded the efficacy of the interventions on core ASD symptoms: social-communication (n=18/21), overall asd symptoms (n=16/21), restricted/repetitive behaviors (n=13/21), disruptive behaviors (n=12/21), adhd symptoms (n=9/21)"
res_adverse = res_p %>%
filter(Outcome %in% safety) %>%
group_by(Outcome, Intervention) %>%
distinct() %>%
summarise(n = n()) %>%
group_by(Intervention) %>%
summarise(n = n())
## `summarise()` has grouped output by 'Outcome'. You can override using the
## `.groups` argument.
paste0("In contrast, only ", nrow(res_adverse[res_adverse$n==3, ]),
" interventions were tested for all our safety outcomes, namely acceptability, tolerability, and the presence of adverse effects (", paste(sort(unlist(res_adverse[res_adverse$n==3, "Intervention"])), collapse=", "), "). Some others were tested for one or two of these outcomes (", paste(sort(unlist(res_adverse[res_adverse$n!=3, "Intervention"])), collapse=", "), ").")
## [1] "In contrast, only 4 interventions were tested for all our safety outcomes, namely acceptability, tolerability, and the presence of adverse effects (MELAT, NAC, OXYT, PUFA). Some others were tested for one or two of these outcomes (L-CARNO, PROB, SULFO, V1a-RA, VIT-D)."
# sort(unique(lapply(res_outcomes[res_outcomes$Outcome %in% safety, "outcome"], function(x) unlist(strsplit(x, ", ")))[[1]]))
# res_outcomes[res_outcomes$Outcome %in% safety, ]
# sort(unique(lapply(res_outcomes[res_outcomes$Outcome %in% safety, "outcome"], function(x) unlist(strsplit(x, ", ")))[[1]]))
# paste0("The outcomes that were covered by the least number of interventions were ", paste(paste0(res_outcomes$Outcome[(nrowOutcome-5):nrowOutcome], " (n=", res_outcomes$n[(nrowOutcome-5):nrowOutcome], "/",length(unique(res_p$Intervention)), ")"), collapse=", "))
paste0("The median number of RCTs per meta-analysis was ",
quantile(res_p$n_studies, 0.5), " (IQR = [",
quantile(res_p$n_studies, 0.25), ", ",
quantile(res_p$n_studies, 0.75), "]), and the median number of participants per meta-analysis was ",
quantile(res_p$total_n, 0.5), " (IQR = [",
quantile(res_p$total_n, 0.25), ", ",
quantile(res_p$total_n, 0.75), "])")
## [1] "The median number of RCTs per meta-analysis was 4 (IQR = [2, 5]), and the median number of participants per meta-analysis was 154 (IQR = [92, 261.25])"
paste0("The median percentage of female per meta-analysis was ",
round(quantile(res_p$perc_female, 0.5, na.rm=TRUE)), "% (IQR = [",
round(quantile(res_p$perc_female, 0.25, na.rm=TRUE)), ", ",
round(quantile(res_p$perc_female, 0.75, na.rm=TRUE)), "])")
## [1] "The median percentage of female per meta-analysis was 14% (IQR = [10, 19])"
tab_Age = as.data.frame(table(res_p$Age))
paste0("A total of ",
tab_Age[tab_Age$Var1=="< 6 yo", "Freq"],
" meta-analyses (",
round(tab_Age[tab_Age$Var1=="< 6 yo", "Freq"] / nrow(res_p) * 100),
"%) involved very young children (<6 years old), ",
tab_Age[tab_Age$Var1== "6-12 yo", "Freq"],
" meta-analyses (",
round(tab_Age[tab_Age$Var1 == "6-12 yo", "Freq"] / nrow(res_p) * 100), "%) involved school-aged children (6-12 years old), and ",
tab_Age[tab_Age$Var1== "13-19 yo", "Freq"], " meta-analyses (",
round(tab_Age[tab_Age$Var1 == "13-19 yo", "Freq"] / nrow(res_p) * 100), "%) involved adolescents (13-19 years old), and ",
tab_Age[tab_Age$Var1== ">= 20 yo", "Freq"], " meta-analyses (",
round(tab_Age[tab_Age$Var1 == ">= 20 yo", "Freq"] / nrow(res_p) * 100), "%) involved adults (>= 20 years old)")
## [1] "A total of 26 meta-analyses (18%) involved very young children (<6 years old), 87 meta-analyses (59%) involved school-aged children (6-12 years old), and 15 meta-analyses (10%) involved adolescents (13-19 years old), and 20 meta-analyses (14%) involved adults (>= 20 years old)"
tab_IQ = as.data.frame(table(res_p$IQ))
paste0("A total of ",
tab_IQ[tab_IQ$Var1=="Average (80-119)", "Freq"], " meta-analyses (",
round(tab_IQ[tab_IQ$Var1=="Average (80-119)", "Freq"] / nrow(res_p) * 100), "%) focused on participants with overall cognitive functioning within the norm (IQ = [80-119]), ",
sum(tab_IQ[tab_IQ$Var1 %in% c("Limit (70-79)", "Low (< 70)"), "Freq"]), " meta-analyses (",
round(sum(tab_IQ[tab_IQ$Var1 %in% c("Limit (70-79)", "Low (< 70)"), "Freq"]) / nrow(res_p) * 100), "%) focused on participant with moderate or important cognitive difficulties (IQ < 80), and ",
nrow(res_p) - sum(tab_IQ$Freq), " (",
round(100-sum(tab_IQ$Freq) / nrow(res_p) * 100), "%) did not provide information on the cognitive functioning of the participants.")
## [1] "A total of 66 meta-analyses (45%) focused on participants with overall cognitive functioning within the norm (IQ = [80-119]), 23 meta-analyses (16%) focused on participant with moderate or important cognitive difficulties (IQ < 80), and 59 (40%) did not provide information on the cognitive functioning of the participants."
res_large = res_p %>% filter(eG > 0.5)
res_sig = res_p %>% filter(as.numeric(p_value) < 0.05)
res_effect = res_p %>% filter(eG > 0 & (eG > 0.5 | as.numeric(p_value) < 0.05))
paste0("The overall results indicated that ",
sum(res_p$eG > 0), " meta-analyses (", propC(res_p$eG > 0),
"%) had a pooled effect size in favor of the experimental group (SMD > 0, OR/RR > 1). More precisely, ",
nrow(res_large), " (",
round(nrow(res_large) / nrow(res_p) * 100) , "%) had a magnitude that can be considered as moderate or large (SMD > 0.50), and ",
nrow(res_sig), " (",
round(nrow(res_sig) / nrow(res_p) * 100) , "%) had a statistically significant p-value (p < 0.05)")
## [1] "The overall results indicated that 113 meta-analyses (76%) had a pooled effect size in favor of the experimental group (SMD > 0, OR/RR > 1). More precisely, 26 (18%) had a magnitude that can be considered as moderate or large (SMD > 0.50), and 33 (22%) had a statistically significant p-value (p < 0.05)"
paste0("It should be acknowledged that the ",
nrow(res_effect), " meta-analyses with moderate to large pooled effect sizes and/or statistically significant results are not without limitations, given that only ",
sum(res_effect$rob > 75), " (", propC(res_effect$rob > 75), "%) of them had more than 75% of their participants coming from low-risk studies.",
" Moreover, ",
sum(res_effect$I2 >= 25), " (", propC(res_effect$I2 >= 25),
"%) of these meta-analyses had a moderate or large inconsistency (I² >= 25%) and only ",
sum(as.numeric(res_effect$PI_lo_g) > 0 | as.numeric(res_effect$PI_up_g) < 0, na.rm=TRUE), " (",
propC(as.numeric(res_effect$PI_lo_g) > 0 | as.numeric(res_effect$PI_up_g) < 0), "%) of the meta-analyses had a 95% prediction interval that excluded the null. Moreover, in ", propC(res_effect$n_agreement < 0.8), "% of the meta-analyses, more than 25% of the individual effect sizes were of the opposite sign to the pooled effect size.")
## [1] "It should be acknowledged that the 38 meta-analyses with moderate to large pooled effect sizes and/or statistically significant results are not without limitations, given that only 5 (13%) of them had more than 75% of their participants coming from low-risk studies. Moreover, 16 (42%) of these meta-analyses had a moderate or large inconsistency (I² >= 25%) and only 5 (13%) of the meta-analyses had a 95% prediction interval that excluded the null. Moreover, in NaN% of the meta-analyses, more than 25% of the individual effect sizes were of the opposite sign to the pooled effect size."
paste0(
ifelse(nrow(res_p %>% filter(eG < 0 & as.numeric(p_value) < 0.05)), " there is a detrimental effect", "there is no detrimental effect"
))
## [1] "there is no detrimental effect"
Overlap
res_over <- res_m %>%
group_by(PICO_amstar) %>%
filter(n() > 1) %>%
ungroup() %>%
mutate(n_SRMA = length(unique(paper))) %>%
group_by(PICO_amstar) %>%
summarise(
n = n(),
n_SRMA = unique(n_SRMA),
paper = collapsunique(paper),
factor = collapsunique(Factor),
min_es = min(eG),
max_es = max(eG),
dif_es = max(eG) - min(eG),
max_grade = head(sort(GRADE), 1),
min_grade = tail(sort(GRADE), 1),
prop_sig = sum(as.numeric(p_value) < 0.05) / n(),
avg_percentage_overlap = mean(map_dbl(combn(n(), 2, simplify = FALSE), function(idx) {
interval_overlap_percentage(
ci_lo_g[idx[1]], ci_up_g[idx[1]],
ci_lo_g[idx[2]], ci_up_g[idx[2]])
})))
res_test = res_m %>%
group_by(PICO_amstar) %>%
filter(n() > 1) %>%
mutate(PICO_hom = grepl("Homo", PICO_amstar_precise, fixed=TRUE)) %>%
filter((PICO_hom & Age_precise == "Homogeneous") | !PICO_hom | IN_meta == 1) %>%
group_by(PICO_amstar) %>%
filter(n() > 1) %>%
mutate(eG_meta_IN = eG[IN_meta == 1],
GRADE_meta_IN = GRADE[IN_meta == 1],
p_value_meta_IN = p_value[IN_meta == 1]) %>%
ungroup() %>% rowwise() %>%
filter(
(abs(eG - eG_meta_IN) >= 0.50 & as.numeric(p_value) != as.numeric(p_value_meta_IN)) |
(GRADE == GRADE_meta_IN & as.numeric(p_value) != as.numeric(p_value_meta_IN)) |
abs(as.numeric(GRADE) - as.numeric(GRADE_meta_IN)) >= 2 |
IN_meta == 1) %>%
# select(c(1:15, PICO_amstar, PICO_amstar_precise, IN_meta, amstar.x, Age, Age_precise, eG_meta_IN, GRADE, GRADE_meta_IN)) %>%
arrange(PICO_amstar)
res_over_factors = res_test %>%
filter(abs(as.numeric(GRADE_meta_IN) - as.numeric(GRADE)) >= 2 |
(abs(eG_meta_IN - eG) >= 0.30 & sum(as.numeric(p_value_meta_IN) < 0.05,
as.numeric(p_value) < 0.05) == 1))
res_over_disc = res_test %>%
filter(Factor %in% res_over_factors$Factor |
PICO_amstar %in% res_over_factors$PICO_amstar & IN_meta==1)
res_m$inter_out = paste0(res_m$Intervention, " - ", res_m$Outcome)
metaumbrella::forest(res_m %>% filter(PICO_amstar %in% res_over_disc$PICO_amstar),
layout = "RevMan5",
subgroup = "inter_out",
subgroup.name = "",
leftcols = c("paper", "Age", "Age_precise", "GRADE",
"n_studies", "rob_num", "active_controls", "I2", "effect.ci"),
leftlabs = c("Paper", "Age", "Age_precise", "GRADE",
"n-studies", "Low RoB", "Active controls", "I²", "eSMD + 95% CI"),
# sortvar = res_p$Age,
smlab = "Key overlapping situations"
)

paste0("Among the ", nrow(res_over), " PICOs that were explored by at least 2 overlapping meta-analyses, the median overlap of the 95% CI of the pooled effect sizes was ",
round(quantile(res_over$avg_percentage_overlap, 0.5)),
"% (IQR=[",
round(quantile(res_over$avg_percentage_overlap, 0.25)),
"%, ",
round(quantile(res_over$avg_percentage_overlap, 0.75)),
"%]). Only ",
sum(res_over$min_grade == res_over$max_grade), " (",
round(sum(res_over$min_grade == res_over$max_grade) / nrow(res_over) * 100),
"%) of PICOs consistently achieved a similar GRADE rating and only ",
sum(res_over$prop_sig %in% c(0,1)), " (",
round(sum(res_over$prop_sig %in% c(0,1)) / nrow(res_over) * 100),
"%) of PICOs consistently achieved a similar statistical significance status.")
## [1] "Among the 53 PICOs that were explored by at least 2 overlapping meta-analyses, the median overlap of the 95% CI of the pooled effect sizes was 55% (IQR=[41%, 68%]). Only 36 (68%) of PICOs consistently achieved a similar GRADE rating and only 36 (68%) of PICOs consistently achieved a similar statistical significance status."
paste0("We found ", length(unique(res_over_disc$PICO_amstar)), " PICOs that presented a large discrepancy regarding the magnitude of the estimated effect (i.e., pooled effect sizes varying by more than SMD >= 0.30 with different statistical significance status), or regarding the confidence or GRADE status differing by two points (very low versus moderate; Fig X.). This information is displayed in details in Supplementary Materials SX, and we will illustrate this overlaping issue with the only PICO that generated ")
## [1] "We found 4 PICOs that presented a large discrepancy regarding the magnitude of the estimated effect (i.e., pooled effect sizes varying by more than SMD >= 0.30 with different statistical significance status), or regarding the confidence or GRADE status differing by two points (very low versus moderate; Fig X.). This information is displayed in details in Supplementary Materials SX, and we will illustrate this overlaping issue with the only PICO that generated "
OXYT restrict
dat_oxyt_rest = dcct %>%
filter(intervention_general == "NAC" &
first_author_meta %in% c("Iffland", "Siafis (child)") &
outcome_general == "Disruptive behaviors") %>%
arrange(author, year) %>%
mutate(TE = ifelse(!is.na(reverse_es), -as.numeric(as.character(value)), value),
TE_lo = ifelse(!is.na(reverse_es), -as.numeric(as.character(ci_up)), ci_lo),
TE_up = ifelse(!is.na(reverse_es), -as.numeric(as.character(ci_lo)), ci_up),
ID = paste0(author, " (", year, ")"))
met_oxyt_res = meta::metagen(TE = as.numeric(TE),
lower = as.numeric(TE_lo),
upper = as.numeric(TE_up),
subgroup = ID,
subgroup.name = "Study",
studlab = meta_review,
data = dat_oxyt_rest)
m2 <- meta::rob(RoB_SeqGen, RoB_Conceal, RoB_BlindPers, RoB_BlindOut, RoB_Attrition, RoB_Reporting, overall = rob, data = met_oxyt_res, tool = "rob1")
meta::forest(m2,
layout = "RevMan5", common = FALSE, random = FALSE,
leftcols = c("studlab", "effect.ci"),
leftlabs = c("Study","SMD [95% CI]"),
col.square = "gray",
col.study = "black", col.square.lines = "black",
overall = FALSE, hetstat = FALSE)


# subgroup = "author",
# subgroup.name = ""
# metaumbrella::forest(focus_oxyt_restrict_adult,
# layout = "RevMan5",
# subgroup = "inter_out",
# subgroup.name = "",
# leftcols = c("paper", "Age", "Age_precise", "GRADE",
# "n_studies", "rob", "active_controls", "I2", "effect.ci"),
# leftlabs = c("Paper", "Age", "Age_precise", "GRADE",
# "n-studies", "Low RoB", "Active controls", "I²", "eSMD + 95% CI"),
# # sortvar = res_p$Age,
# smlab = "Key overlapping situations"
# )
res_over_sig <- res_m %>%
group_by(PICO_amstar) %>%
filter(n() > 1) %>%
mutate(eG_meta_IN = eG[IN_meta == 1],
GRADE_meta_IN = GRADE[IN_meta == 1],
p_value_meta_IN = p_value[IN_meta == 1]) %>%
filter(as.numeric(p_value_meta_IN) < 0.05) %>%
summarise(
n = n(),
paper = collapsunique(paper),
factor = collapsunique(Factor),
min_es = min(eG),
max_es = max(eG),
dif_es = max(eG) - min(eG),
max_grade = head(sort(GRADE), 1),
min_grade = tail(sort(GRADE), 1),
prop_sig = sum(as.numeric(p_value) < 0.05) / n(),
avg_percentage_overlap = mean(map_dbl(combn(n(), 2, simplify = FALSE), function(idx) {
interval_overlap_percentage(
ci_lo_g[idx[1]], ci_up_g[idx[1]],
ci_lo_g[idx[2]], ci_up_g[idx[2]])
})))
nrow(res_over_sig %>% filter(prop_sig < 1)) / nrow(res_over_sig)
## [1] 0.6
res_discrepant = res_m %>% filter(PICO_amstar %in% res_over_disc$PICO_amstar)
metaumbrella::forest(res_discrepant,
layout = "RevMan5",
subgroup = "PICO_amstar",
subgroup.name = "",
leftcols = c("Intervention", "Outcome", "Age_precise", "paper",
"GRADE",
"n_studies", "rob_num",
"I2", "effect.ci"),
leftlabs = c("Intervention", "Outcome", "Homogeneous Age", "Paper",
"GRADE",
"n-studies", "Low RoB",
"I²", "eSMD + 95% CI"),
# sortvar = res_p$Age,
smlab = "Discrepant PICOs"
)

propC((res_over$min_grade == "Very low" & res_over$max_grade == "Moderate") |
res_over$dif_es>= 0.50 & !res_over$prop_sig %in% c(0, 1))
## [1] 15
paste0("The median overlap per meta-analysis was ",
quantile(res_over$avg_percentage_overlap, 0.5), " (IQR = [",
quantile(res_over$avg_percentage_overlap, 0.25), ", ",
quantile(res_over$avg_percentage_overlap, 0.75), "])")
## [1] "The median overlap per meta-analysis was 55.394641564084 (IQR = [41.0098522167488, 68.2401532502682])"
average_consistency <- dat %>%
group_by(intervention) %>%
summarise(avg_percentage_overlap = mean(map_dbl(combn(n(), 2, simplify = FALSE), function(idx) {
interval_overlap_percentage(
lower_bound[idx[1]], upper_bound[idx[1]],
lower_bound[idx[2]], upper_bound[idx[2]])
})))
res_m_cred = metaumbrella::add.evidence(res,
criteria = "Personalized",
class_I = c(total_n = 500, p_value = 1e-3, I2 = 50,
egger_p = .05, pi = "notnull",
rob = 75),
class_II = c(total_n = 350, p_value = 1e-3,
largest_CI = "notnull",
rob = 50),
class_III = c(total_n = 200, p_value = 1e-3),
class_IV = c(p_value = 5e-2), verbose = FALSE)